package com.duomn.mybatis.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class ReflectUtil {

	/////////////// Field /////////////////
    private static void makeAccessible(Field field) {
        if (!Modifier.isPublic(field.getModifiers())) {
            field.setAccessible(true);
        }
    }

    private static Field getDeclaredField(Object object, String filedName) {
        for (Class<?> superClass = object.getClass(); superClass != Object.class; superClass = superClass
                .getSuperclass()) {
            try {
                return superClass.getDeclaredField(filedName);
            } catch (NoSuchFieldException e) {
                // Field 不在当前类定义, 继续向上转型
            }
        }
        return null;
    }

    public static void setFieldValue(Object object, String fieldName, Object value) {
        Field field = getDeclaredField(object, fieldName);

        if (field == null)
            throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");

        makeAccessible(field);

        try {
            field.set(object, value);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public static Object getFieldValue(Object object, String fieldName) {
        Field field = getDeclaredField(object, fieldName);
        if (field == null)
            throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");

        makeAccessible(field);

        Object result = null;
        try {
            result = field.get(object);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return result;
    }
    
    /////////////// Method /////////////////
    private static Method getDeclaredMethod(String methodName, Object object, Class<?>[] paramTypes, Object[] args) {
        for (Class<?> superClass = object.getClass(); superClass != Object.class; superClass = superClass
                .getSuperclass()) {
            try {
            	if (paramTypes != null && paramTypes.length > 0) { // 有参数
            		return superClass.getDeclaredMethod(methodName, paramTypes);
            	} else { // 无参数
            		return superClass.getDeclaredMethod(methodName);
            	}
            } catch (SecurityException e) {
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				// 向上层父类查询
			}
        }
        return null;
    }
    
    private static Object invokeMethod(String methodName, Object target, Class<?>[] paramTypes, Object[] args) {
    	Object result = null;
    	try {
    		// 递归从所有父类中查找方法
			Method method = getDeclaredMethod(methodName, target, paramTypes, args);
			if (!Modifier.isPublic(method.getModifiers())) {
	            method.setAccessible(true);
	        }
			result = method.invoke(target, args);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
    	return result;
    }
    
    /** 无参数 */
    public static Object invokeMethod(String methodName, Object target) {
    	return invokeMethod(methodName, target, new Class<?>[0], new Object[0]);
    }
    
    /** 
     * 带一个方法参数
     * @param methodName 方法名称
     * @param target 方法所在的对象
     * @param c1 方法第一个参数的类型
     * @param o1 方法的第一个参数
     * @return
     */
    public static Object invokeMethod(String methodName, Object target,  Class<?> c1, Object o1) {
    	return invokeMethod(methodName, target, new Class<?>[] {c1}, new Object[] {o1});
    }
    
    /** 带两个参数*/
    public static Object invokeMethod(String methodName, Object target,  Class<?> c1, Object o1, Class<?> c2, Object o2) {
    	return invokeMethod(methodName, target, new Class<?>[] {c1, c2}, new Object[] {o1, c2});
    }
    
    /** 带三个参数 */
    public static Object invokeMethod(String methodName, Object target,  Class<?> c1, Object o1, Class<?> c2, Object o2, Class<?> c3, Object o3) {
    	return invokeMethod(methodName, target, new Class<?>[] {c1, c2, c3}, new Object[] {o1, c2, c3});
    }
    
    /** 带四个参数 */
    public static Object invokeMethod(String methodName, Object target,  Class<?> c1, Object o1, Class<?> c2, Object o2, Class<?> c3, Object o3, Class<?> c4, Object o4) {
    	return invokeMethod(methodName, target, new Class<?>[] {c1, c2, c3, c4}, new Object[] {o1, c2, c3, c4});
    }
    
}
